/**
 * @file    gt98xx_drv_efc.c
 * @author  Giantec-Semi ATE
 * @brief   Source file of EFC driver module.
 * @version 0.1
 * 
 * @copyright Copyright (c) 2021 Giantec-Semi
 */

#include "gt98xx_drv_efc.h"

/**
 * @addtogroup GT98xx_DRV
 * @{
 */

/**
 * @addtogroup EFC_DRV
 * @{
 */

/**
 * @addtogroup EFC_DRV_Exported_Functions
 * @{
 */

/**
 * @addtogroup EFC_DRV_EF_Init
 * @{
 */

/**
 * @fn ErrorStatus void DrvEfcInit(EfcTypedef* efc_instance, DrvEfcInitTypedef* efc_init_struct)
 * @brief Initialize EFC registers according to the specified parameters in efc_init_struct.
 * 
 * @param[in] efc_instance EFC instance
 * @param[in] efc_init_struct Pointer to a @ref DrvEfcInitTypedef structure which contains the 
 *            configuration information of EFC.
 * @return EFC is initialized or not
 * @retval kSuccess EFC registers are initialized according to efc_init_struct
 * @retval kError Error occured during EFC registers initialization
 */
ErrorStatus DrvEfcInit(EfcTypedef* efc_instance, DrvEfcInitTypedef* efc_init_struct) {
  ErrorStatus status = kSuccess;

  /* Check the parameters */
  assert_param(IS_EFC_INSTANCE(efc_instance));

  /* Unlock */
  DrvEfcUnlock(efc_instance, kParmAndSectorProtectionReg | kMainArrayAccess | kNvrAccess | kErase);
  /* Enable all main array */
  DrvEfcEnableSector1To32Access(efc_instance, 0xFFFFFFFF);
  DrvEfcEnableSector33To64Access(efc_instance, 0xFFFFFFFF);
  DrvEfcEnableSector65To96Access(efc_instance, 0xFFFFFFFF);
  DrvEfcEnableSector97To128Access(efc_instance, 0xFFFFFFFF);
  DrvEfcEnableSector129To160Access(efc_instance, 0xFFFFFFFF);
  DrvEfcEnableSector161To192Access(efc_instance, 0xFFFFFFFF);
  DrvEfcEnableSector193To224Access(efc_instance, 0xFFFFFFFF);
  DrvEfcEnableSector225To256Access(efc_instance, 0xFFFFFFFF);
  /* Configure CFG0~CFG6 */
  uint32_t cfg0 = ((efc_init_struct->trw & 0xFF) << EFC_CFG0_TRW_Pos) |
                  ((efc_init_struct->trc & 0xFF) << EFC_CFG0_TRC_Pos) |
                  ((efc_init_struct->tprog & 0xFFFF) << EFC_CFG0_TPROG_Pos);
  uint32_t cfg3 = ((efc_init_struct->tpgs & 0xFFFF) << EFC_CFG3_TPGS_Pos) |
                  ((efc_init_struct->tpgh & 0xFFFF) << EFC_CFG3_TPGH_Pos);
  uint32_t cfg4 = ((efc_init_struct->tnvs & 0xFFFF) << EFC_CFG4_TNVS_Pos) |
                  ((efc_init_struct->trcv_prog & 0xFFFF) << EFC_CFG4_TRCV_PROG_Pos);
  uint32_t cfg5 = ((efc_init_struct->trcv_erase_sector & 0xFFFF) << EFC_CFG5_TRCV_ERASE_SECTOR_Pos) |
                  ((efc_init_struct->trcv_erase_chip & 0xFFFF) << EFC_CFG5_TRCV_ERASE_CHIP_Pos);
//  WRITE_REG(efc_instance->CFG0, ((efc_init_struct->trw & 0xFF) << EFC_CFG0_TRW_Pos) |
//                                ((efc_init_struct->trc & 0xFF) << EFC_CFG0_TRC_Pos) |
//                                ((efc_init_struct->tprog & 0xFFFF) << EFC_CFG0_TPROG_Pos));
  WRITE_REG(efc_instance->CFG0, cfg0);
  WRITE_REG(efc_instance->CFG1, efc_init_struct->sec);
  WRITE_REG(efc_instance->CFG2, efc_init_struct->cec);
  WRITE_REG(efc_instance->CFG3, cfg3);
  WRITE_REG(efc_instance->CFG4, cfg4);
  WRITE_REG(efc_instance->CFG5, cfg5);
//  WRITE_REG(efc_instance->CFG3, ((efc_init_struct->tpgs & 0xFFFF) << EFC_CFG3_TPGS_Pos) |
//                                ((efc_init_struct->tpgh & 0xFFFF) << EFC_CFG3_TPGH_Pos));
//  WRITE_REG(efc_instance->CFG4, ((efc_init_struct->tnvs & 0xFFFF) << EFC_CFG4_TNVS_Pos) |
//                                ((efc_init_struct->trcv_prog & 0xFFFF) << EFC_CFG4_TRCV_PROG_Pos));
//  WRITE_REG(efc_instance->CFG5, ((efc_init_struct->trcv_erase_sector & 0xFFFF) << EFC_CFG5_TRCV_ERASE_SECTOR_Pos) |
//                                ((efc_init_struct->trcv_erase_chip & 0xFFFF) << EFC_CFG5_TRCV_ERASE_CHIP_Pos));
  DrvEfcSetTwup(efc_instance, efc_init_struct->twup);
  
  return status;
}

static uint32_t DrvEfcDiv(uint32_t divisor, uint32_t dividend) {
  uint32_t quotient = 0;
  while (divisor >= dividend) {
    quotient++;
    divisor -= dividend;
  }
  return quotient;
}

/**
 * @fn void DrvEfcStructInit(DrvEfcInitTypedef* efc_init_struct)
 * @brief Set each @ref DrvEfcInitTypedef field to default value.
 * 
 * @param[out] efc_init_struct Pointer to a @ref DrvEfcInitTypedef structure
 *             which field will be set to default values.
 */
void DrvEfcStructInit(DrvEfcInitTypedef* efc_init_struct) {
  ///@todo 使用减法替代除法?

  /* Trw, CFG0[7:0], >= 100ns, set 128ns*/
  efc_init_struct->trw = DrvEfcDiv(SystemCoreClock >> 2, 1953125UL);       // Clock * 128 / 1000000000
  /* Trc, CFG0[15:8], >= 25ns, set 32ns*/
  efc_init_struct->trc = DrvEfcDiv(SystemCoreClock >> 4, 1953125UL);       // Clock * 30 / 1000000000
  /* Tprog, CFG0[31:16], >= 6us and <= 7.5us, set 7us*/
  efc_init_struct->tprog = DrvEfcDiv((SystemCoreClock * 7) >> 6, 15625);      // Clock * 7 / 1000000
  /* SEC, CFG1, >= 4ms and <= 5ms, set 4.5ms*/
  efc_init_struct->sec = ((SystemCoreClock << 2) + (SystemCoreClock >> 1)) / 1000;
  /* CEC, CFG2, >= 20ms and <= 40ms, set 32ms*/
  efc_init_struct->cec = (SystemCoreClock << 5) / 1000;
  /* Tpgs, CFG3[15:0], >= 5us and <= 6.25us, set 6us*/
  efc_init_struct->tpgs = DrvEfcDiv((SystemCoreClock * 6) >> 6, 15625);       // Clock * 6 / 1000000
  /* Tpgh, CFG3[31:16], >= 15ns, set 24ns*/
  efc_init_struct->tpgh = DrvEfcDiv((SystemCoreClock * 24) >> 9, 1953125UL);  // Clock * 24 / 1000000000
  /* Tnvs, CFG4[15:0], >= 4us, set 8us*/
  efc_init_struct->tnvs = DrvEfcDiv(SystemCoreClock >> 3, 15625);             // Clock * 8 / 1000000
  /* Trcv_prog, CFG4[31:16], >= 5us, set 8us */
  efc_init_struct->trcv_prog = efc_init_struct->tnvs;
  /* Trcv_erase_sector, CFG5[15:0], >= 50us, set 64us*/
  efc_init_struct->trcv_erase_sector = DrvEfcDiv(SystemCoreClock, 15625);     // Clock * 64 / 1000000
  /* Trcv_erase_chip, CFG5[31:16], >= 200us, set 256us*/
  efc_init_struct->trcv_erase_chip = DrvEfcDiv(SystemCoreClock << 2, 15625);  // Clock * 256 / 1000000
  /* Twup, CFG6[15:0], >= 2us, set 4us*/
  efc_init_struct->twup = DrvEfcDiv(SystemCoreClock >> 4, 15625);             // Clock * 4 / 1000000
}

/** @} EFC_DRV_EF_Init */
/** @} EFC_DRV_Exported_Functions */
/** @} EFC_DRV */
/** @} GT98xx_DRV */
